﻿using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Linq;


#if UNITY_EDITOR
using UnityEditor;
#endif


#if UNITY_EDITOR
[CanEditMultipleObjects]
#endif
public class PlistAnimation : ScriptableObject
{
    [Header("---Plist Animation Config")]
    public AnimationClip Clip;
    public string AnimationName;
    public string AnimationPath;



#if UNITY_EDITOR
    [Header("---Create Plist Animation")]
    public string AnimationSpritePath;
    public int FrameRate = 30;
    public bool LoopTime = false;


    private Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>();
    private Dictionary<string, Dictionary<float, string>> bindingData = new Dictionary<string, Dictionary<float, string>>();


    public void FindAllSprites()
    {
        string[] guids = AssetDatabase.FindAssets(AnimationName, new string[] { AnimationPath });
        guids = DelRepeatData(guids);
        foreach (string guid in guids)
        {
            Texture2D texture2D = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(guid));
            if (texture2D)
            {
                Object[] objs = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(texture2D));
                foreach (Object o in objs)
                {
                    if (o.GetType() == typeof(Sprite))
                    {
                        sprites[o.name] = o as Sprite;
                    }
                }
            }
        }
        //SaveAnimationClipBindings();
        //ReplaceAnimation();
    }
    public void CreatePlistAnimation()
    {
        FindAllSprites();

        if (!Clip)
        {
            Clip = new AnimationClip();
            string[] paths = AssetDatabase.GetAssetPath(this).Split('/');
            paths[paths.Length - 1] = "";
            AssetDatabase.CreateAsset(Clip, string.Join("/", paths) + "/" + AnimationName + ".anim");
        }

        EditorCurveBinding curveBinding = new EditorCurveBinding();
        curveBinding.type = typeof(SpriteRenderer);
        curveBinding.path = AnimationSpritePath;
        curveBinding.propertyName = "m_Sprite";
        ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[sprites.Count];
        Clip.frameRate = FrameRate;

        for (int i = 0; i < sprites.Count; i++)
        {
            ObjectReferenceKeyframe objectReferenceKeyframe = new ObjectReferenceKeyframe();
            objectReferenceKeyframe.time = (float)i / (float)FrameRate;
            objectReferenceKeyframe.value = sprites[AnimationName + "_" + i.ToString("000")];
            keyFrames[i] = objectReferenceKeyframe;
        }

        SerializedObject serializedClip = new SerializedObject(Clip);
        AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings"));
        clipSettings.loopTime = LoopTime;
        serializedClip.ApplyModifiedProperties();
        AnimationUtility.SetObjectReferenceCurve(Clip, curveBinding, keyFrames);
        AssetDatabase.SaveAssets();
    }

    public void ClearSpritesAndRecord()
    {
        bindingData.Clear();
        sprites.Clear();
    }

    public void SaveAnimationClipBindings()
    {
        EditorCurveBinding[] bindings = AnimationUtility.GetObjectReferenceCurveBindings(Clip);
        foreach (EditorCurveBinding binding in bindings)
        {
            if (binding.type == typeof(SpriteRenderer))
            {
                string pathName = binding.path + "/" + binding.propertyName;
                if (bindingData.ContainsKey(pathName))
                {
                    bindingData[pathName].Clear();
                }
                else
                {
                    bindingData[pathName] = new Dictionary<float, string>();
                }

                foreach (ObjectReferenceKeyframe o in AnimationUtility.GetObjectReferenceCurve(Clip, binding))
                {
                    bindingData[pathName].Add(o.time, o.value ? o.value.name : "");
                }
            }
        }
    }
    public void ReplaceAnimation()
    {
        EditorCurveBinding[] bindings = AnimationUtility.GetObjectReferenceCurveBindings(Clip);
        foreach (EditorCurveBinding binding in bindings)
        {
            if (binding.type == typeof(SpriteRenderer))
            {
                string pathName = binding.path + "/" + binding.propertyName;
                if (bindingData.ContainsKey(pathName))
                {
                    List<ObjectReferenceKeyframe> objectReferenceKeyframes = new List<ObjectReferenceKeyframe>();
                    foreach (float time in bindingData[pathName].Keys)
                    {
                        ObjectReferenceKeyframe objectReferenceKeyframe = new ObjectReferenceKeyframe();
                        objectReferenceKeyframe.time = time;
                        if (sprites.ContainsKey(bindingData[pathName][time]))
                        {
                            objectReferenceKeyframe.value = sprites[bindingData[pathName][time]];
                        }

                        objectReferenceKeyframes.Add(objectReferenceKeyframe);
                    }
                    AnimationUtility.SetObjectReferenceCurve(Clip, binding, objectReferenceKeyframes.ToArray());
                }
            }
        }
    }

    //[MenuItem("Assets/Rain/PlistAnimation")]
    public static void CreateAsset()
    {
        ScriptableObjectUtility.CreateAsset<PlistAnimation>();
    }

    private string[] DelRepeatData(string[] array)
    {
        return array.GroupBy(p => p).Select(p => p.Key).ToArray();
    }

    private void OnValidate()
    {

    }
#endif
}


#if UNITY_EDITOR
[CustomEditor(typeof(PlistAnimation))]
class AudioEventEditor : Editor
{
    public override void OnInspectorGUI()
    {
        PlistAnimation ae = (PlistAnimation)target;


        if (GUILayout.Button("Update Animation Resources"))
        {
            ae.FindAllSprites();
        }

        if (GUILayout.Button("Create Plist Animation"))
        {
            ae.CreatePlistAnimation();
        }

        if (GUILayout.Button("Record Animation Data"))
        {
            ae.SaveAnimationClipBindings();
        }

        if (GUILayout.Button("Update Animation"))
        {
            if (ae.Clip)
            {
                ae.ReplaceAnimation();
            }
            else
            {
                Debug.LogError("Clip is null.");
            }

        }

        if (GUILayout.Button("Clear Record Data And Sprites"))
        {
            ae.ClearSpritesAndRecord();
        }

        DrawDefaultInspector();
    }
}
#endif


#if UNITY_EDITOR
class AnimationClipSettings
{
    SerializedProperty m_Property;
    private SerializedProperty Get(string property) { return m_Property.FindPropertyRelative(property); }
    public AnimationClipSettings(SerializedProperty prop) { m_Property = prop; }

    public float startTime { get { return Get("m_StartTime").floatValue; } set { Get("m_StartTime").floatValue = value; } }
    public float stopTime { get { return Get("m_StopTime").floatValue; } set { Get("m_StopTime").floatValue = value; } }
    public float orientationOffsetY { get { return Get("m_OrientationOffsetY").floatValue; } set { Get("m_OrientationOffsetY").floatValue = value; } }
    public float level { get { return Get("m_Level").floatValue; } set { Get("m_Level").floatValue = value; } }
    public float cycleOffset { get { return Get("m_CycleOffset").floatValue; } set { Get("m_CycleOffset").floatValue = value; } }

    public bool loopTime { get { return Get("m_LoopTime").boolValue; } set { Get("m_LoopTime").boolValue = value; } }
    public bool loopBlend { get { return Get("m_LoopBlend").boolValue; } set { Get("m_LoopBlend").boolValue = value; } }
    public bool loopBlendOrientation { get { return Get("m_LoopBlendOrientation").boolValue; } set { Get("m_LoopBlendOrientation").boolValue = value; } }
    public bool loopBlendPositionY { get { return Get("m_LoopBlendPositionY").boolValue; } set { Get("m_LoopBlendPositionY").boolValue = value; } }
    public bool loopBlendPositionXZ { get { return Get("m_LoopBlendPositionXZ").boolValue; } set { Get("m_LoopBlendPositionXZ").boolValue = value; } }
    public bool keepOriginalOrientation { get { return Get("m_KeepOriginalOrientation").boolValue; } set { Get("m_KeepOriginalOrientation").boolValue = value; } }
    public bool keepOriginalPositionY { get { return Get("m_KeepOriginalPositionY").boolValue; } set { Get("m_KeepOriginalPositionY").boolValue = value; } }
    public bool keepOriginalPositionXZ { get { return Get("m_KeepOriginalPositionXZ").boolValue; } set { Get("m_KeepOriginalPositionXZ").boolValue = value; } }
    public bool heightFromFeet { get { return Get("m_HeightFromFeet").boolValue; } set { Get("m_HeightFromFeet").boolValue = value; } }
    public bool mirror { get { return Get("m_Mirror").boolValue; } set { Get("m_Mirror").boolValue = value; } }
}


public static class ScriptableObjectUtility
{
    /// <summary>
    //	This makes it easy to create, name and place unique new ScriptableObject asset files.
    /// </summary>
    public static void CreateAsset<T>() where T : ScriptableObject
    {
        T asset = ScriptableObject.CreateInstance<T>();
        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        if (path == "")
        {
            path = "Assets";
        }
        else if (Path.GetExtension(path) != "")
        {
            path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
        }

        string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New " + typeof(T).ToString() + ".asset");

        AssetDatabase.CreateAsset(asset, assetPathAndName);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}
#endif